/**
 *  Copyright (c) 1997-2013, www.tinygroup.org (luo_guo@icloud.com).
 *
 *  Licensed under the GPL, Version 3.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.gnu.org/licenses/gpl.html
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.tinygroup.dbf.impl;

import org.tinygroup.dbf.*;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by luoguo on 2014/7/10.
 */
public class StreamDbfReader implements DbfReader {
    private FileChannel fileChannel;

    public DbfDocument readDbf(String fileName, String encode) throws IOException {
        return readDbf(new File(fileName), encode);
    }

    private void skipHeaderTerminator() throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocate(1);
        readByteBuffer(byteBuffer);
        if (byteBuffer.array()[0] != HEADER_END_CHAR) {
            throw new IOException("头结束符期望是13，但实际是：" + byteBuffer.array()[0]);
        }
    }

    protected List<DbfField> readFields(DbfDocument dbfDocument) throws IOException {
        ArrayList<DbfField> fields = new ArrayList<DbfField>();
        for (int i = 0; i < (dbfDocument.getHeader().getHeaderLength() - HEADER_LENGTH - 1) / FIELD_LENGTH; i++) {
            fields.add(readField(dbfDocument));
        }
        return fields;
    }

    protected void readByteBuffer(ByteBuffer byteBuffer) throws IOException {
        fileChannel.read(byteBuffer);
    }

    protected DbfField readField(DbfDocument dbfDocument) throws IOException {
        DbfField field = new DbfField();
        ByteBuffer byteBuffer = ByteBuffer.allocate(FIELD_LENGTH);
        readByteBuffer(byteBuffer);
        byte[] bytes = byteBuffer.array();
        field.setName(new String(bytes, 0, NAME_LENGTH, dbfDocument.getEncode()).trim().split("\0")[0]);
        field.setType((char) bytes[NAME_LENGTH]);
        field.setDisplacement(DbfUtil.getUnsignedInt(bytes, DISPLACEMENT_POSITION, DISPLACEMENT_LENGTH));
        field.setLength(DbfUtil.getUnsignedInt(bytes, FIELD_LENGTH_POSITON, 1));
        field.setDecimal(DbfUtil.getUnsignedInt(bytes, FIELD_DECIMAL_POSITON, 1));
        field.setFlag(bytes[FIELD_FLAG_POSITION]);
        return field;
    }

    protected DbfHeader readHeader() throws IOException {
        DbfHeader header = new DbfHeader();
        ByteBuffer byteBuffer = ByteBuffer.allocate(HEADER_LENGTH - 1);
        readByteBuffer(byteBuffer);
        byte[] bytes = byteBuffer.array();
        header.setLastUpdate((DbfUtil.getUnsignedInt(bytes, 0, 1) + START_YEAR) * YEAR_POS + DbfUtil.getUnsignedInt(bytes, 1, 1) * MONTH_POS + DbfUtil.getUnsignedInt(bytes, 2, 1));
        header.setRecordCount(DbfUtil.getUnsignedInt(bytes, RECORD_COUNT_POSITION, RECORD_COUNT_LENGTH));
        header.setHeaderLength(DbfUtil.getUnsignedInt(bytes, HEADER_LENGTH_POSITION, HEADER_LENGTH_LENGTH));
        header.setRecordLength(DbfUtil.getUnsignedInt(bytes, RECORD_LENGTH_POSITION, RECORD_LENGTH_LENGTH));
        return header;
    }

    public DbfDocument readDbf(File file, String encode) throws IOException {
        RandomAccessFile aFile = new RandomAccessFile(file, "r");
        fileChannel = aFile.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1);
        fileChannel.read(byteBuffer);
        DbfDocument dbfDocument = new DbfDocument();
        dbfDocument.setType(byteBuffer.array()[0]);
        dbfDocument.setEncode(encode);
        dbfDocument.setHeader(readHeader());
        dbfDocument.setFields(readFields(dbfDocument));
        dbfDocument.setDataSet(new StreamDbfDataSet(fileChannel, dbfDocument));
        skipHeaderTerminator();
        return dbfDocument;
    }

    public void close() throws IOException {
        fileChannel.close();
    }
}
